//package generics.simple;
//
//import java.util.ArrayList;
//import java.util.List;
//
//public class Main {
//
//    public static void main(String[] args) {
//        /*
//        1.1
//            Java泛型默认禁止协变
//            如以下三行代码，如果第一行不报错，那么在运行时第三行代码会报更严重的类型错误。
//            所以Java编译器在第一行直接报编译错误可以避免以上问题的存在。也就是泛型禁止协变，泛型是不变
//         */
//        List<Animal> animalList = new ArrayList<Dog>(); //编译报错
//        animalList.add(new Dog());
//        animalList.add(new Cat());
//        /*
//        1.2
//            Java1.0时没有泛型，Java设计者又希望能对数组进行通用处理，
//            导致如果数组不支持协变的话，很多方法都需要为每一种类型写逻辑了，
//            这显然不合理，所以最初将数组设计为支持协变
//
//            但数组支持协变就会导致1.1所述的安全隐患，例如以下代码，当运行到第三行时，
//            报出ArrayStoreException异常。
//            “允许数组协变是Java的缺陷！”
//         */
//        Animal[] animalArray = new Dog[10];
//        animalArray[0] = new Dog();
//        animalArray[1] = new Cat(); // ArrayStoreException
//        /*
//        1.3
//            我希望有一个方法，传入Animal或Animal的子类集合，给集合内的所有动物都喂饭（调用他们的eat()），这个方法应该如何设计？
//            如果泛型不支持协变，那么我们就需要创建processCats(List<Cat> cats)和processDogs(List<Dog> dogs)，
//            如果将来还有Mouse类，我们就还要补充上processMouses(List<Mouse> mouses)，这种代码将无法维护。
//
//            为了满足通用处理的需求，泛型必须要有支持协变的机制，由此Java引入了通配符 ?,
//            我们直接调用processAnimals(List<? extends Animal> animals)方法，既可以传入List<Dog> dogs，又可以传入List<Cat> cats；
//            将来如果有新的类，只要它继承了Animal类，那么它的泛型集合就能被此方法处理
//
//            由于List<? extends Animal>限定了存储元素类型的上界是Animal，所以在processAnimals(List<? extends Animal> animals)方法中，
//            我们可以调用Animal的成员方法
//            由于List<? extends Animal>只限定了存储元素类型的上界是Animal, 没有限定存储元素类型的下界, 导致传入的集合可能是List<Cat> cats或
//            List<Dog> dogs, 存入元素大概率会导致类型错误, 所以编译器禁止这种集合中存储元素.
//         */
//        List<Dog> dogs = new ArrayList<Dog>();
//        dogs.add(new Dog());
//        dogs.add(new Dog());
//        processAnimals(dogs);
//
//        List<Cat> cats = new ArrayList<Cat>();
//        cats.add(new Cat());
//        cats.add(new Cat());
//        processAnimals(cats);
//        /*
//        1.4
//            List<? extends Animal> animals只规定泛型的上界, 无法向这样的集合中存储元素
//            Dog类对象可以放到List<Dog>集合中, 对于一个需求: 传入一个狗的集合, 希望筛选出开心的狗, 并放到传入的狗集合中, 那么copyHappyDog1(List<? extends Dog> src, List<Dog> dest)就可以满足需求
//            Dog类对象可以放到List<Animal>集合中, 对于一个新的需求: 传入一个狗的集合, 希望筛选出开心的狗, 并放到传入的动物集合中, 那么我们就需要新写一个方法copyHappyDog2(List<? extends Dog> src, List<Animal> dest)来满足新的需求
//            Dog类对象可以放到List<Object>集合中, 对于一个新的...
//            其实我们可以使用List<? super Dog>来规定dest的泛型的下界, 这样我们就不用针对每次的新需求去写新的代码了
//
//            注意List<? super Dog>只规定dest的泛型的下界, 所以读出来的元素只能当Object对象处理
//         */
//        List<Dog> dogList = new ArrayList<Dog>();
//        dogList.add(new Corgi(true));
//        dogList.add(new Corgi(false));
//        dogList.add(new Akita(false));
//        copyHappyDog1(dogList, new ArrayList<Dog>());
//        copyHappyDog1(dogList, new ArrayList<Animal>()); // 编译报错
//        copyHappyDog2(dogList, new ArrayList<Animal>());
//        copyHappyDog2(dogList, new ArrayList<Object>()); // 编译报错
//
//
//        copyHappyDog(dogList, new ArrayList<Dog>());
//        copyHappyDog(dogList, new ArrayList<Animal>());
//        copyHappyDog(dogList, new ArrayList<Object>());
//        /*
//        1.5
//            PECS原则(Producer Extends Consumer Super)的解释
//            以copyHappyDog(List<? extends Dog> src, List<? super Dog> dest)为例
//            List<? extends Dog> src: 源列表是一个生产者，允许读取Dog或其子类型的数据。
//            List<? super Dog> dest: 目标列表是一个消费者，允许写入Dog或其子类型的数据(这里并没有写错, 请看changeList(List<? extends Corgi> src, List<? super Dog> dest)方法去理解"允许写入Dog或其子类型的数据"的含义)
//            Collections的public static <T> void copy(List<? super T> dest, List<? extends T> src)就很好地体现了这个原则
//
//            对于泛型的协变<? extends Animal>, 处理逻辑是只读不写
//            对于泛型的逆变<? super Animal>, 处理逻辑是只写不读
//            如果要求既读又写, 那么必须是准确的泛型, 而不能使用通配符
//         */
//
//        /*
//        1.6
//            解释List<E>接口中，add要求入参为E，而remove和contains的入参却是Object
//            boolean add(E e);
//            boolean remove(Object o);
//            boolean contains(Object o);
//            List<E>本身类型安全的要求（要求存储的元素类型必须是E或继承了E的子类），所以add方法入参要求是E
//            但对于remove和contains这种不影响存储的元素类型一致性的方法，不必要求方法入参必须是E或继承了E的子类，所以入参为Object即可
//         */
//    }
//
//    public static void processCats(List<Cat> cats) {
//        for (Cat cat : cats) {
//            cat.eat();
//        }
//    }
//
//    public static void processDogs(List<Dog> dogs) {
//        for (Dog dog : dogs) {
//            dog.eat();
//        }
//    }
//
//    public static void processAnimals(List<? extends Animal> animals) {
//        for (Animal animal : animals) {
//            animal.eat();
//        }
//    }
//
//    public static void copyHappyDog1(List<? extends Dog> src, List<Dog> dest) {
//        for (Dog dog : src) {
//            if (dog.isHappy()) {
//                dest.add(dog);
//            }
//        }
//    }
//
//    public static void copyHappyDog2(List<? extends Dog> src, List<Animal> dest) {
//        for (Dog dog : src) {
//            if (dog.isHappy()) {
//                dest.add(dog);
//            }
//        }
//    }
//
//    public static void copyHappyDog(List<? extends Dog> src, List<? super Dog> dest) {
//        for (Dog dog : src) {
//            if (dog.isHappy()) {
//                dest.add(dog);
//            }
//        }
//    }
//
//    /**
//     * dest集合可以存储Dog类以及Dog子类的对象, 因为src的泛型规定了上界为Corgi, 所以方法中的dest.add(corgi);不会报错
//     * 这个例子解释了1.5中 "List<? super Dog> dest: 目标列表是一个消费者，允许写入Dog或其子类型的数据"
//     * @param src
//     * @param dest
//     */
//    public static void changeList(List<? extends Corgi> src, List<? super Dog> dest) {
//        for (Corgi corgi : src) {
//            dest.add(corgi);
//        }
//    }
//}
